# pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>

#include <unistd.h>

#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num=10;//线程池中线程的数目
template<class T>//任务类型
class ThreadPool//单例模式的线程池，只能有一个线程池
{
public://一、维护任务队列
    pthread_mutex_t *getMutex()
    {
        return lock;
    }

    bool isEmpty()
    {
        return task_queue_.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&cond,&lock);//等待条件变量cond满足，当cond满足后，且有空闲锁的，就会唤醒该线程。
                                       //若cond不满足，则会让线程在等待锁资源的队列中等待
    }

    T getTask()
    {
        T t=task_queue_.front();
        task_queue_.pop();//头删
        return t;
    }

private://二、维护线程vector、维护线程池
    ThreadPool(int thread_num=g_thread_num):num_(num)
    {
        pthread_mutex_init(&lock,nullptr);
        pthread_cond_init(&cond,nullptr);
        for(int i=1;i<=num_;i++)
        {
            threads_.push_back(new Thraed(i,rountine,this));//直接传this当做线程的参数即可，因为this已经包含了外部传的信息
        }
    }
    ThreadPool(const ThreadPool<T>& other)=delete;//保证单例模式，单例不能被拷贝
    const ThreadPool<T>& operator=(const ThreadPool<T>& other)=delete;//保证单例模式，单例不能被赋值

public://三、维护单例(满足单线程，多线程情况下的单例模式的使用)
    static ThreadPool<T> *getThreadPool(int num=g_thread_num)
    {
        //在多线程模式下，线程池对象就是一个共享资源了，可以被多个线程同时访问到，此时就需要加锁进行访问创建了
        if(nullptr==thread_ptr_)//判断线程池对象是否创建了，若没创建则需要创建。这一层if的判断仅是读的，不会对共享资源做修改，所以这层if不需要加锁
        {
            //1.两层if的原因:
            //两层if的原因，因为在判断线程对象是否创建的时候是不用锁的，如果只有一层if的话，则每次判断线程对象是否创建了都需要加锁解锁，效率低下。
            //2.两层if的好处:
            //有了两层之后，第一层if判断只是纯判断，不修改，不需要加锁。第二层if是对共享资源修改的，需要加锁。这样做到了判断和修改解耦，
            //不需要在判断时频繁加锁和解锁。
            lockGuard lockguard(&mutex);
            if(nullptr==thread_ptr_)//判断线程池对象是否创建了，若没创建则需要创建。这一层if的执行内容是需要对共享资源进行修改的了，所以这层if需要加锁
            {
                thread_ptr=new ThreadPool<T>(num);
                return thread_ptr;
            }
        }
        return thread_ptr;
    }   

    void run()//启动线程池
    {
        for(auto& iter:threads_)
        {
            iter->start();
            logMessage(NORMAL,"%s %s",iter->name().c_str(),"启动成功");
        }
    }
    static void* routine(void* args)//线程例程函数-从任务队列取出任务，执行任务
    {
        ThreadData* td=(ThreadData*)args;
        ThreadPool<T> *tp=(ThreadPool<T>*)td->args_;
        while(true)
        {
            T task;
            {
                lockGuard(tp->getMutex())
                
                while(tp->isEmpty())//任务队列为空就等待，因为任务队列的任务可能会被抢，所以唤醒之后也不一定有任务用来执行，所以要进行多次判断，
                                    //直到真的有任务可以执行
                tp->waitCond();
                
                //读取任务
                task=tp->getTask();//任务队列是共享的，所以要加锁访问。从任务队列中拿出任务，拿到线程自己的私有空间，之后操作就不需要加锁了
            }
            task(td->name_);
        }
    }
    
    void pushTask(const T& task)
    {
        lockGuard lockguard(&lock);
        task_queue_.push(task);
        pthread_cond_signal(&cond);//任务队列不为空了，则唤醒即可
        
    }
private:
    std::vector<Thread*> threads_;//线程vector
    int num_;//线程池中线程的数量
    std::queue<T> Task_queue_;//任务队列
    
    static ThreadPool<T> *thread_ptr_;//线程池对象,在外部用getThreadPool获取该对象
    static pthread_mutex_t mutex_;//任务队列的锁


    pthread_mutex_t lock_;//线程之间的锁
    pthread_cond_t cond_;//
};